package com.hgyc.mom.tool.strategy;

import com.hgyc.mom.common.util.DateUtils;
import com.hgyc.mom.common.util.StringUtils;
import com.hgyc.mom.tool.entity.AutoCodePart;
import com.hgyc.mom.tool.entity.AutoCodeResult;
import com.hgyc.mom.tool.entity.AutoCodeRule;
import com.hgyc.mom.tool.enums.PartTypeEnum;
import com.hgyc.mom.tool.service.AutoCodePartService;
import com.hgyc.mom.tool.service.AutoCodeResultService;
import com.hgyc.mom.tool.service.AutoCodeRuleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.StringSubstitutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 编码规则生成工具类
 * @author fwj
 * @since 2025/5/8
 */
@Slf4j
@Service
public class AutoCodeUtil {
    public static ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();

    @Autowired
    private AutoCodeRuleService autoCodeRuleService;

    @Autowired
    private AutoCodePartService autoCodePartService;

    @Autowired
    private AutoCodeResultService autoCodeResultService;

    @Autowired
    private PartTypeHandler partTypeHandler;

    private String lastSerialNo;

    synchronized
    public String genSerialCode(String ruleCode, Map<String,String> paramValue){
        //查找编码规则
        AutoCodeRule rule = autoCodeRuleService.getByCode(ruleCode);
        Assert.notNull(rule, String.format("未获取到指定类型:[%s]的业务编码生成规则",ruleCode));

        //查找规则组成
        List<AutoCodePart> parts = autoCodePartService.getByRuleId(rule.getId());
        List<AutoCodePart> collect = parts.stream().filter(part-> PartTypeEnum.PART_TYPE_SERIALNO.getCode().equals(part.getPartType())).collect(Collectors.toList());

        Assert.isTrue(collect.size()<2, String.format("编码规则[%s]流水号方式的组成只能存在一个",ruleCode));

        StringBuilder buff = new StringBuilder();
        parts.forEach(codePart ->{
            if (StringUtils.equals(codePart.getPartType(),PartTypeEnum.PART_TYPE_INPUTCHAR.getCode())) {
                if(paramValue.containsKey(codePart.getInputCharacter())) {
                    codePart.setInputCharacter(StringUtils.trim(paramValue.get(codePart.getInputCharacter())));
                }
            }

            //根据当前组成部分，获取当前组成部分的结果
            String partStr = partTypeHandler.choiceExecute(codePart);

            //如果是流水号部分，则进行记录
            if(StringUtils.equals(codePart.getPartType(),PartTypeEnum.PART_TYPE_SERIALNO.getCode())){
                lastSerialNo = partStr;
            }
            //将获取到的部分组装进整体编码中
            buff.append(partStr);
        });

        Assert.hasText(buff.toString(),String.format("规则：[%s]生成的编码为空！",ruleCode));

        String autoCode = paddingStr(rule,buff);

        //将生成结果保存到数据库
        saveAutoCodeResult(rule,autoCode, paramValue == null ? null : paramValue.toString());
        return autoCode;
    }

    synchronized
    public List<String> batchGenerateCode(String ruleCode, Map<String,String> paramValue, Integer qty) {
        List<String> codeList = new ArrayList<>();
        String lastCode = null;

        //查找编码规则
        AutoCodeRule rule = autoCodeRuleService.getByCode(ruleCode);
        Assert.notNull(rule, String.format("未获取到指定类型:[%s]的业务编码生成规则",ruleCode));

        //查找规则组成
        List<AutoCodePart> parts = autoCodePartService.getByRuleId(rule.getId());
        List<AutoCodePart> collect = parts.stream().filter(part-> PartTypeEnum.PART_TYPE_SERIALNO.getCode().equals(part.getPartType())).collect(Collectors.toList());

        Assert.isTrue(collect.size()<2, String.format("编码规则[%s]流水号方式的组成只能存在一个",ruleCode));

        StringBuilder buff = new StringBuilder();
        parts.forEach(codePart ->{
            if (StringUtils.equals(codePart.getPartType(),PartTypeEnum.PART_TYPE_INPUTCHAR.getCode())) {
                if(paramValue.containsKey(codePart.getInputCharacter())) {
                    codePart.setInputCharacter(StringUtils.trim(paramValue.get(codePart.getInputCharacter())));
                }
            }

            //根据当前组成部分，获取当前组成部分的结果
            String partStr = partTypeHandler.choiceExecute(codePart);

            //如果是流水号部分，则进行记录
            if(StringUtils.equals(codePart.getPartType(),PartTypeEnum.PART_TYPE_SERIALNO.getCode())){
                lastSerialNo = partStr;
                buff.append("${lastSerialNo}");
            } else {
                //将获取到的部分组装进整体编码中
                buff.append(partStr);
            }
        });

        Assert.hasText(buff.toString(),String.format("规则：[%s]生成的编码为空！",ruleCode));

        String autoCode = paddingStr(rule,buff);
        Map<String, String> valuesMap = new HashMap<>();
        Long serialNo = Long.parseLong(lastSerialNo);
        valuesMap.put("lastSerialNo", lastSerialNo);

        AutoCodePart autoCodePart = collect.get(0);
        for (int i = 0; i < qty; i++) {
            String newCode = autoCode;
            if (StringUtils.isNotEmpty(lastSerialNo)) {
                log.info("生成序列号：{}", lastSerialNo);

                // 将 valuesMap 的更新提前
                valuesMap.put("lastSerialNo", lastSerialNo);
                newCode = StringSubstitutor.replace(autoCode, valuesMap);

                // 最后一次不添加
                if (i < qty - 1) {
                    serialNo = serialNo + autoCodePart.getSerialStep();
                    lastSerialNo = String.format("%0" + autoCodePart.getPartLength() + "d", serialNo);
                }
            }
            codeList.add(newCode);
            lastCode = newCode;
        }

        //将生成结果保存到数据库
        saveAutoCodeResult(rule, lastCode, paramValue == null ? null : paramValue.toString());
        return codeList;
    }

    /**
     * 根据编码规则的配置进行补齐操作
     * @param rule
     * @param sb
     * @return
     */
    private String paddingStr(AutoCodeRule rule,StringBuilder sb){
        String isPadding = rule.getIsPadded();
        if("Y".equals(isPadding)){
            int maxLength = rule.getMaxLength();
            String paddingChar = rule.getPaddedChar();
            StringBuilder resultStr = new StringBuilder();
            long length = maxLength - sb.length();
            Assert.isTrue(maxLength>sb.length(), String.format("生成的编码[%s]已经超出规则中配置的最大长度：[%d]", sb,maxLength));

            if("L".equals(rule.getPaddedMethod())){
                //左补齐
                //使用指定字符补齐左侧后,再将生成的编码添加到右侧
                for(;length>0;length --){
                    resultStr.append(paddingChar);
                }
                resultStr.append(sb);
            }else{
                //右补齐
                //将生成的编码添加到左侧后,再使用指定字符补齐右侧
                resultStr.append(sb);
                for(;length>0;length --){
                    resultStr.append(paddingChar);
                }
            }
            return resultStr.toString();
        }
        return sb.toString(); //如果不需要补齐，则直接返回
    }

    private void saveAutoCodeResult(AutoCodeRule rule,String autoCode,String inputChar){
        Boolean flag = threadLocal.get(); //针对当前线程的判断 flag = true则数据库中没有当前规则的生成记录
        if(flag !=null && flag){
            AutoCodeResult rs = new AutoCodeResult();
            rs.setRuleId(rule.getId());
            rs.setGenDate(DateUtils.format(LocalDateTime.now(),"yyyyMMddHHmmss"));
            rs.setLastResult(autoCode);
            rs.setGenIndex(1L);
            rs.setLastSerialNo(Long.valueOf(lastSerialNo));
            rs.setLastInputChar(inputChar);
            autoCodeResultService.save(rs);
        }else{
            //直接更新对应的记录（我们默认非流水号模式下一个RULE_CODE只有一种方式）
            List<AutoCodeResult> results = autoCodeResultService.getByRuleId(rule.getId());
            Assert.notEmpty(results, String.format("未查询到规则{%s}对应的结果记录",rule.getRuleCode()));
            AutoCodeResult rs = results.get(0);
            rs.setLastResult(autoCode);
            rs.setGenDate(DateUtils.format(LocalDateTime.now(),"yyyyMMddHHmmss"));
            rs.setGenIndex(rs.getGenIndex()+1);
            rs.setLastSerialNo(Long.valueOf(lastSerialNo));
            rs.setLastInputChar(inputChar);
            autoCodeResultService.updateById(rs);
        }

    }
}
